package com.sailing.xjpb.orm.repository;

import com.sailing.xjpb.common.sercurity.SailingUserContext;
import com.sailing.xjpb.orm.utils.SailingTenantSqlParser;
import org.hibernate.transform.ResultTransformer;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import javax.persistence.EntityManager;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * EntityManager完成对数据库的query和update-对jpa进行补充<p/>
 * 所有业务代码中的类，如果使用了@PostConstruct注解的方法进行了数据库查询，则该类上需要加上注解@DependsOn("sailingDataBaseInfoHandler")，
 * 否则会因为SailingTenantTableInitialization该类还未进行初始化，拦截器TenantStatementInterceptor中使用该类会报空指针异常
 *
 * @author YaoWei
 * @see com.sailing.xjpb.orm.intercepter.TenantStatementInterceptor
 * @see com.sailing.xjpb.orm.intercepter.SailingTenantTableInitialization
 */
public class SailingTenantRepository extends SailingRepository {

    /**
     * 用户上下文
     */
    private static SailingUserContext sailingUserContext;

    /**
     * 设置用户上下文信息
     *
     * @param sailingUserContext 用户上下文
     */
    public static void setUserContext(SailingUserContext sailingUserContext) {
        if (sailingUserContext != null) {
            SailingTenantRepository.sailingUserContext = sailingUserContext;
        }
    }

    public SailingTenantRepository(EntityManager entityManager) {
        super(entityManager);
    }

    /**
     * 执行sql语句
     *
     * @param exSql  sql语句
     * @param params 注入参数
     * @return 受影响行数
     */
    @Override
    public int executeUpdate(String exSql, Map<String, Object> params) {
        return doTenantQuery(exSql, sql -> super.executeUpdate(sql, params));
    }

    /**
     * 将查询结果封装为List<Map<String,Object>>对象
     *
     * @param querySql    sql语句
     * @param params      参数
     * @param transformer 结果转换器
     * @return 返回sql结果
     */
    @Override
    public List<Map<String, Object>> listMap(String querySql, Map<String, Object> params,
                                             ResultTransformer transformer) {
        return doTenantQuery(querySql, sql -> super.listMap(sql, params, transformer));
    }

    /**
     * 将查询结果封装为List<Map<String,Object>>对象---since 2021-06-07 修改未查询到结果初始化一个map返回
     *
     * @param querySql sql语句
     * @param params   参数
     * @return 返回sql结果
     */
    @Override
    public Map<String, Object> map(String querySql, Map<String, Object> params, ResultTransformer transformer) {
        return doTenantQuery(querySql, sql -> super.map(sql, params, transformer));
    }

    /**
     * 将查询结果封装为T对象
     * since 2021-06-07 结果未查询到时返回Optional.empty();
     *
     * @param querySql    sql语句
     * @param elementType 返回结果集元素类型
     */
    @Override
    public <T> Optional<T> queryV1(String querySql, Class<T> elementType) {
        return doTenantQuery(querySql, sql -> super.queryV1(sql, elementType));
    }

    /**
     * 将查询结果封装为Page<Map<String, Object>对象
     *
     * @param pageable pageable 分页对象
     * @param querySql sql语句
     * @param params   参数
     */
    @Override
    public Page<Map<String, Object>> page(Pageable pageable, String querySql, Map<String, Object> params) {
        return doTenantQuery(querySql, sql -> super.page(pageable, sql, params));
    }

    /**
     * 将查询结果封装为Page<Map<String, Object>对象
     *
     * @param pageable    pageable 分页对象
     * @param querySql    sql语句
     * @param params      参数
     * @param transformer 结果转换器
     */
    @Override
    public Page<Map<String, Object>> page(Pageable pageable, String querySql, Map<String, Object> params,
                                          ResultTransformer transformer) {
        return doTenantQuery(querySql, sql -> super.page(pageable, sql, params, transformer));
    }

    /**
     * 查询结果为一个对象
     *
     * @param querySql       查询语句
     * @param paramsSupplier 注入参数生产函数
     * @param elementType    返回结果元素类型
     * @param <T>            返回结果元素类型
     */
    @Override
    protected <T> T query(String querySql, Supplier<Object> paramsSupplier, Class<T> elementType) {
        return doTenantQuery(querySql, sql -> super.query(sql, paramsSupplier, elementType));
    }

    /**
     * 将查询结果封装为Page<T>对象
     *
     * @param pageable       pageable 分页对象
     * @param querySql       sql语句
     * @param paramsSupplier 参数
     * @param elementType    返回结果集元素类型
     */
    @Override
    protected <T> Page<T> page(Pageable pageable, String querySql, Supplier<Object> paramsSupplier,
                               Class<T> elementType) {
        return doTenantQuery(querySql, sql -> super.page(pageable, sql, paramsSupplier, elementType));
    }

    /**
     * 将查询结果封装为List<T>对象
     *
     * @param pageable       pageable分页对象
     * @param querySql       sql语句
     * @param paramsSupplier 获取参数的生成函数
     * @param elementType    返回结果集元素类型
     */
    @Override
    protected <T> List<T> list(Pageable pageable, String querySql, Supplier<Object> paramsSupplier,
                               Class<T> elementType) {
        return doTenantQuery(querySql, sql -> super.list(pageable, sql, paramsSupplier, elementType));
    }

    /**
     * 将查询结果封装为List<T>对象
     *
     * @param querySql       sql语句
     * @param paramsSupplier 获取参数的生成函数
     * @param elementType    返回结果集元素类型
     */
    @Override
    protected <T> List<T> list(String querySql, Supplier<Object> paramsSupplier, Class<T> elementType) {
        return doTenantQuery(querySql, sql -> super.list(sql, paramsSupplier, elementType));
    }

    /**
     * 查询统计
     *
     * @param querySql       查询语句
     * @param paramsSupplier 获取参数的生成函数
     */
    @Override
    protected Long count(String querySql, Supplier<Object> paramsSupplier) {
        return doTenantQuery(querySql, sql -> super.count(sql, paramsSupplier));
    }

    protected <T> T doTenantQuery(String sql, Function<String, T> func) {
        String tenantQuerySql = getTenantQuerySql(sql);
        return func.apply(tenantQuerySql);
    }

    /**
     * 获取租户sql
     *
     * @param querySql 原始查询sql
     * @return 租户sql
     */
    private String getTenantQuerySql(String querySql) {
        String tenantCode = sailingUserContext.getTenantCode();
        return SailingTenantSqlParser.getTenantSql(querySql, tenantCode);
    }
}
